﻿//(c) Alexandru Ghiondea, 2011
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using TestLibrary;

namespace TestRunner
{
    class Program
    {
        static void Main(string[] args)
        {
            

            //the assemblies to test are in args.

            //for testing we point to JsonTest.dll 
            args = new string[] { 
                "..\\..\\..\\JsonTests\\bin\\Debug\\JsonTests.dll",
                "..\\..\\..\\DynamicJSONDeserializer\\bin\\Debug\\DynamicJSONDeserializer.dll",
            };

            if (args.Length == 0)
                return;
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Dictionary<string, List<TestCase>> tests = new Dictionary<string, List<TestCase>>();
            foreach (var file in args)
            {
                Assembly assembly = null;
                try
                {
                    assembly = Assembly.LoadFrom(file);
                }
                catch
                {
                    Console.WriteLine("Skipping over " + file);
                }

                var currentAssemblyTests = getTestMethods(assembly);

                mergeTests(ref tests, currentAssemblyTests);

            }

            if (tests.Count == 0)
            {
                Console.WriteLine("No tests found. Exiting.");
                return;

            }
            //Figure out the longest text so we know how to align all tests (and PASS values)
            var biggestText = Math.Min(100, (from k in tests.Values
                                             select k into y
                                             from t in y
                                             select t).Max((t) => t.Test.Length)) + 4;





            StringBuilder sb = new StringBuilder();

            int test = 0, fail = 0;
            //invoke all methods
            foreach (var key in tests.Keys)
            {
                Console.WriteLine(key);
                foreach (var testcase in tests[key])
                {
                    test++;
                    Console.Write(("  " + testcase.Test + " ").PadRight(biggestText, ' '));

                    try
                    {
                        testcase.Method.Invoke(null, null);
                        displayString("Pass", ConsoleColor.Green);
                    }
                    catch (Exception e)
                    {
                        fail++;
                        displayString("Fail", ConsoleColor.Red);

                        sb.AppendLine("+++  " + key + " (" + testcase.Test + ") -- Fail");
                        sb.AppendLine(e.InnerException.ToString());
                    }

                }
            }
            sw.Stop();

            Console.WriteLine();
            Console.WriteLine("Tests: " + test + " Passed: " + (test - fail) + " Failed: " + fail);
            Console.WriteLine(string.Format("Time elapsed: {0}ms", sw.ElapsedMilliseconds));

            if (fail != 0)
            {
                Console.WriteLine();
                Console.WriteLine("Error Log:");
                Console.WriteLine(sb.ToString());
            }
        }

        /// <summary>
        /// Merges the groups of tests into a single bigger dictionary
        /// </summary>
        /// <param name="tests"></param>
        /// <param name="currentAssemblyTests"></param>
        private static void mergeTests(ref Dictionary<string, List<TestCase>> tests, Dictionary<string, List<TestCase>> currentAssemblyTests)
        {
            foreach (var key in currentAssemblyTests.Keys)
            {
                if (!tests.ContainsKey(key))
                    tests.Add(key, new List<TestCase>());

                tests[key].AddRange(currentAssemblyTests[key]);
            }

        }

        private static Dictionary<string, List<TestCase>> getTestMethods(Assembly assembly)
        {

            Dictionary<string, List<TestCase>> tests = new Dictionary<string, List<TestCase>>();
            if (assembly == null)
                return tests;

            //figure out the types
            var types = assembly.GetTypes();

            var testClasses = from t in types
                              let attributes = t.GetCustomAttributes(typeof(TestGroup), false)
                              where attributes.Count() > 0
                              select new { type = t, attribute = attributes[0] as TestGroup };


            foreach (var cls in testClasses)
            {
                if (!tests.ContainsKey(cls.attribute.TestName))
                    tests.Add(cls.attribute.TestName, new List<TestCase>());

                var methods = from c in cls.type.GetMethods(BindingFlags.Public | BindingFlags.Static)
                              let attribs = c.GetCustomAttributes(typeof(TestCaseAttribute), false)
                              where attribs.Count() > 0
                              select new { method = c, attrib = attribs[0] as TestCaseAttribute };

                foreach (var m in methods)
                {
                    //For negative tests, add a visual indicator
                    tests[cls.attribute.TestName].Add(new TestCase() { Test = m.attrib.TestName + (m.attrib.ExpectedResult != TestcaseType.Positive ? "*" : ""), Method = m.method });
                }
            }
            return tests;
        }

        private static void displayString(string Text, ConsoleColor color)
        {
            var tempCol = Console.ForegroundColor;
            Console.ForegroundColor = color;
            Console.WriteLine(Text);
            Console.ForegroundColor = tempCol;
        }
    }
}
